#  This file is part of OpenLightingDesigner.
# 
#  OpenLightingDesigner is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  OpenLightingDesigner is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with OpenLightingDesigner.  If not, see <http://www.gnu.org/licenses/>.
#
# Group.py
# Copyright (c) 2010 - 2011 Matthew Reid

from Bases import setID
from Bases import BaseObject, OSCBaseObject, ChildGroup

class DummyGroup(BaseObject):
    _saved_class_name = 'MemberOrderObj'
    _saved_attributes = ['id', 'member_index']
    def __init__(self, **kwargs):
        self._id = None
        #self._member = None
        self.member_index = None
        super(DummyGroup, self).__init__(**kwargs)
        self.group = kwargs.get('group')
        self.member = kwargs.get('member')
        if 'deserialize' not in kwargs:
            if self.id is None:
                self.id = kwargs.get('id')
            if self.member_index is None:
                self.member_index = kwargs.get('member_index')
    @property
    def id(self):
        if True:#self.member is None:
            return self._id
        return self.member.id
    @id.setter
    def id(self, value):
        self._id = value
    @property
    def member(self):
        return self._member
    @member.setter
    def member(self, value):
        self._member = value
        if value is not None:
            #print 'member set Index=%s, memberindex=%s' % (self.Index, self.member.Index)
            self.member_index = value.Index
            self._id = value.id

class GroupBase(OSCBaseObject):
    _saved_attributes = ['base_class', 'id', 'name', 'keep_when_empty', 'membership_lock']
    _saved_child_objects = ['member_order']
    _ChildGroups = {'member_order':{'child_class':DummyGroup}}
    _Properties = {'id':dict(type=str), 'name':dict(type=str)}
    _SettingsProperties = ['id', 'Index', 'name']
    def __init__(self, **kwargs):
        self.base_class = 'Group'
        self.members = {}
        #self.member_order = ChildGroup(name='member_order', child_class=DummyGroup)
        #self._name = None
        self._membership_lock = None
        self._name_set_by_osc = True
        self._props_set_by_osc = set()
        self._osc_address_attr = kwargs.get('osc_address_attr', 'Index')
        kwargs.setdefault('osc_address', kwargs.get(self._osc_address_attr))
        super(GroupBase, self).__init__(**kwargs)
        self.users = set()
        self.register_signal('member_update', 'no_users', 'no_members')
        if 'deserialize' not in kwargs:
            self.id = setID(kwargs.get('id'))
            self.name = kwargs.get('name', self.__class__.__name__)
            self.keep_when_empty = kwargs.get('keep_when_empty', False)
            self.membership_lock = kwargs.get('membership_lock', False)
        self.parent_group = kwargs.get('parent_group')
        if self.parent_group is None:
            self.valid = self.search_for_members(**kwargs)
        else:
            self.valid = False
        #self.add_osc_handler(callbacks={'name-change':self.on_osc_name_change})
        
        #self.bind(name=self.on_name_set)
        
        self.add_osc_handler(callbacks={'property-changed':self.on_osc_property_changed})
        self.bind(property_changed=self._on_own_property_changed)
        
    def _on_own_property_changed(self, **kwargs):
        prop = kwargs.get('Property')
        value = kwargs.get('value')
        if self.osc_enabled and prop.name not in self._props_set_by_osc:
            self.osc_node.send_message(address='property-changed', value=[prop.name, value])
        self._props_set_by_osc.discard(prop.name)
        if prop.name == self._osc_address_attr and self.osc_address != str(value):
            self.set_osc_address(str(value))
            
    def on_osc_property_changed(self, **kwargs):
        if len(kwargs.get('values')) != 2:
            return
        name, value = kwargs.get('values')
        self._props_set_by_osc.add(name)
        setattr(self, name, value)
            
#    def on_name_set(self, **kwargs):
#        name = kwargs.get('value')
#        if self.osc_enabled and not self._name_set_by_osc:
#            self.osc_node.send_message(address='name-change', value=name)
#        self._name_set_by_osc = False
#        self.set_osc_address(name)
#        #self.emit('property_changed', obj=self, property='name')
        
        
#    @property
#    def name(self):
#        return self._name
#    @name.setter
#    def name(self, value):
#        if self.osc_enabled and not self._name_set_by_osc:
#            self.osc_node.send_message(address='name-change', value=value)
#        self._name_set_by_osc = False
#        self.set_osc_address(value)
#        self._name = value
#        self.emit('property_changed', obj=self, property='name')
    
    @property
    def membership_lock(self):
        return self._membership_lock
    @membership_lock.setter
    def membership_lock(self, value):
        self._membership_lock = value
        
    def unlink(self):
        #for member in self.members.itervalues():
        #    member.del_user(self)
        for key in self.members.keys()[:]:
            self.del_member(key)
        super(GroupBase, self).unlink()
            
    def add_user(self, user):
        self.users.add(user)
    def del_user(self, user):
        self.users.discard(user)
        if len(self.users) == 0 and not self.keep_when_empty:
            self.emit('no_users', Group=self)
    def add_member(self, **kwargs):
        if not self.membership_lock:
            for key, val in kwargs.iteritems():
                if key in self.members or key is None:
                    continue
                if key in self.member_order:
                    self.member_order[key].member = val
                else:
                    self.member_order.add_child(member=val, group=self, id=key)
                self.members.update({key:val})
                if hasattr(val, 'add_user'):
                    val.add_user(self)
                    if isinstance(val, GroupBase):
                        val.connect('no_users', self.on_member_no_users)
                        val.connect('no_members', self.on_member_no_members)
                self.emit('member_update', Group=self, new=val)
            
    def del_member(self, key):
        if not self.membership_lock and key in self.members:
            member = self.members[key]
            if hasattr(member, 'del_user'):
                member.del_user(self)
            del self.members[key]
            obj = self.member_order.get(key)
            if obj:
                self.member_order.del_child(obj)
            self.emit('member_update', Group=self, deleted=member)
            if len(self.members) == 0 and not self.keep_when_empty:
                self.emit('no_members', Group=self)
        #print 'key not found', key, self
    def search_for_members(self, **kwargs):
        pass
        
    def on_member_no_users(self, **kwargs):
        Group = kwargs.get('Group')
        print 'group %s, no users' % (Group)
    def on_member_no_members(self, **kwargs):
        Group = kwargs.get('Group')
        print 'group %s no members'
        if Group and Group.id in self.members:
            self.del_member(Group.id)
            
#    def on_osc_name_change(self, **kwargs):
#        value = kwargs.get('values')[0]
#        if value != self.name:
#            self._name_set_by_osc = True
#            self.name = value
            
    def search_for_obj(self, objkey):
        if objkey == self.id:
            return self
        if objkey in self.members:
            return self.members[objkey]
        for m in self.members.itervalues():
            if getattr(m, 'base_class') == 'Group':
                result = m.search_for_obj(objkey)
                if result:
                    return result
        return False
        
    def _deserialize_child(self, d):
        if d['saved_class_name'] == 'MemberOrderObj':
            i = d['attrs']['Index']
            return self.member_order.add_child(group=self, Index=i, deserialize=d)
        return super(Group, self)._deserialize_child(d)

